/***************************************************************************
 *   Copyright (C) 2008 by Thomas Fannes                                   *
 *   thomasfannes@gmail.com                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "HexGrid.h"
#include "IllegalBoardException.h"

namespace structure
{
	HexGrid::HexGrid()
	{
	}

	HexGrid::~HexGrid()
	{
	}

	GridLocationType HexGrid::CheckLocationType(const HexVector & vct)
	{
		int x = vct.getX();
		int y = vct.getY();
		int z = vct.getZ();

		if(x%6 == 0 && y%6 == 0 && z%6 == 0)
			return GridLocationHexagon;
		else if(x%3 == 0 && y%3 == 0 && z%3 == 0)
			return GridLocationSide;
		else if(x%2 == 0 && y%2 == 0 && z%2 == 0)
			return GridLocationCorner;
		else
			return GridLocationInvalid;
	}

	CornerType HexGrid::CheckCornerType(const HexVector & vct)
	{
		if(CheckLocationType(vct) != GridLocationCorner)
			return CornerTypeInvalid;

		HexVector tmp = HexVector(vct.getX() + 6, vct.getY(), vct.getZ());

		if(CheckLocationType(tmp) == GridLocationHexagon)
			return CornerTypeST;
		else
			return CornerTypeBT;
	}

	SideType HexGrid::CheckSideType(const HexVector & vct)
	{
		if(CheckLocationType(vct) != GridLocationSide)
			return SideTypeInvalid;

		if(vct.getX() % 2 == 0)
			return SideType3;
		else if(vct.getY()%2 == 0)
			return SideType5;
		else if(vct.getZ()%2 == 0)
			return SideType1;
		else
			return SideTypeInvalid;
	}

	Corner * HexGrid::createCorner(const HexVector & vct)
	{
		if(CheckLocationType(vct) != GridLocationCorner)
			throw error::IllegalBoardException::CreateNoCornerException();

		Corner * corner = getCornerAt(vct);

		// check if the corner already exists
		if(corner != 0)
			return corner;

		// create the corner
		corner = new Corner(vct);

		linkCorner(corner);

		arElements[vct] = corner;
		return corner;
	}

	Hexagon * HexGrid::createHexagon(const HexVector & vct)
	{
		if(CheckLocationType(vct) != GridLocationHexagon)
			throw error::IllegalBoardException::CreateNoHexagonException();

		Hexagon * hexagon = getHexagonAt(vct);

		// check if the hexagon already exists
		if(hexagon != 0)
			return hexagon;

		// create the hexagon
		hexagon = new Hexagon(vct);

		linkHexagon(hexagon);

		arElements[vct] = hexagon;
		return hexagon;
	}

	Side * HexGrid::createSide(const HexVector & vct)
	{
		if(CheckLocationType(vct) != GridLocationSide)
			throw error::IllegalBoardException::CreateNoSideException();

		Side * side = getSideAt(vct);

		// check if the side already exists
		if(side != 0)
			return side;

		// create the side
		side = new Side(vct);

		linkSide(side);

		arElements[vct] = side;
		return side;
	}


	Corner * HexGrid::getCornerAt(const HexVector & vct) const
	{
		if(CheckLocationType(vct) != GridLocationCorner)
			throw error::IllegalBoardException::CreateNoCornerException();

		return dynamic_cast<Corner *>(getElementAt(vct));
	}

	Hexagon * HexGrid::getHexagonAt(const HexVector & vct) const
	{
		if(CheckLocationType(vct) != GridLocationHexagon)
			throw error::IllegalBoardException::CreateNoHexagonException();

		return dynamic_cast<Hexagon *>(getElementAt(vct));
	}

	Side * HexGrid::getSideAt(const HexVector & vct) const
	{
		if(CheckLocationType(vct) != GridLocationSide)
			throw error::IllegalBoardException::CreateNoSideException();

		return dynamic_cast<Side *>(getElementAt(vct));
	}

	Element * HexGrid::getElementAt(const HexVector & vct) const
	{
		QMap<HexVector, Element *>::const_iterator i = arElements.find(vct);

		if(i == arElements.end())
			return 0;
		else
			return i.value();
	}

	void HexGrid::linkCorner(Corner * corner)
	{
		HexVector pos = corner->getPosition();
		int type = corner->getType();

		// set the surrounding corners
		for(int i = 0; i < 3; i++)
		{
			corner->setHexagon(getHexagonAt(pos + Corner::AdjHexagons[type][i]), i);
			corner->setCorner(getCornerAt(pos + Corner::AdjCorners[type][i]), i);
			corner->setSide(getSideAt(pos + Corner::AdjSides[type][i]), i);
		}
	}

	void HexGrid::linkSide(Side * side)
	{
		HexVector pos = side->getPosition();
		int type = side->getType();

		for(int i = 0; i < 2; i++)
		{
			side->setHexagon(getHexagonAt(pos + Side::AdjHexagons[type][i]), i);
			side->setCorner(getCornerAt(pos + Side::AdjCorners[type][i]), i);
			side->setSide(getSideAt(pos + Side::AdjSides[type][i]), i);
			side->setSide(getSideAt(pos + Side::AdjSides[type][2 + i]), 2+i);
		}
	}

	void HexGrid::linkHexagon(Hexagon * hexagon)
	{
		HexVector pos = hexagon->getPosition();

		for(int i = 0; i < 6; i++)
		{
			hexagon->setHexagon(getHexagonAt(pos + Hexagon::AdjHexagons[i]), i);
			hexagon->setCorner(getCornerAt(pos + Hexagon::AdjCorners[i]), i);
			hexagon->setSide(getSideAt(pos + Hexagon::AdjSides[i]), i);
		}
	}

	bool HexGrid::elementExistsAt(const HexVector & vct) const
	{
		return !(arElements.find(vct) == arElements.end());
	}
}
